#include "StdAfx.hpp"
#include "Context.hpp"


Context g_Context;

VariableManager::VarData::VarData(Type type, bool Const, bool allocData)
: VarType(type)
, Const(Const)
, Data(allocData ? type.GetSize() : 0)
{
	if (!Data.empty())
		VarType.AllocValue(&Data[0]);
}

VariableManager::VarData::~VarData()
{
	if (!Data.empty())
		VarType.FreeValue(&Data[0]);
}

VariableManager::VariableManager()
: m_Working(false)
{
}

VariableManager::~VariableManager()
{
}

bool VariableManager::Find(const string &name, Type *outType, bool *outConst)
{
	VarData *varData = FindVar(name);
	if (varData)
	{
		if (outType) *outType = varData->VarType;
		if (outConst) *outConst = varData->Const;
		return true;
	}
	else
		return false;
}

bool VariableManager::FindLocal(const string &name, Type *outType, bool *outConst)
{
	VarData *varData = FindVarLocal(name);
	if (varData)
	{
		if (outType) *outType = varData->VarType;
		if (outConst) *outConst = varData->Const;
		return true;
	}
	else
		return false;
}

void * VariableManager::AccessValue(const string &name)
{
	VarData *varData = FindVar(name);
	assert(m_Working && varData && !varData->Data.empty());
	return &varData->Data[0];
}

void VariableManager::BeginBlock()
{
	m_Stack.push_back(VarMapPtr(new VarMap()));
}

void VariableManager::EndBlock()
{
	assert(!m_Stack.empty());
	m_Stack.pop_back();
}

VariableManager::VarData * VariableManager::FindVar( const string &name )
{
	for (uint i = m_Stack.size(); i--; )
	{
		VarMap::iterator it = m_Stack[i]->find(name);
		if (it != m_Stack[i]->end())
			return it->second.get();
	}
	return NULL;
}

VariableManager::VarData * VariableManager::FindVarLocal( const string &name )
{
	VarMap &localMap = *m_Stack.back().get();
	VarMap::iterator it = localMap.find(name);
	if (it != localMap.end())
		return it->second.get();
	return NULL;
}

void * VariableManager::New( const string &name, Type type, bool Const)
{
	VarMap &localMap = *m_Stack.back().get();
	assert(localMap.find(name) == localMap.end());

	VarPtr varData(new VarData(type, Const, m_Working));
	localMap.insert(VarMap::value_type(name, varData));
	return varData->Data.empty() ? NULL : &varData->Data[0];
}

void VariableManager::AddMathConstants()
{
	float *addr;
	addr = (float*)New("PI", Type(Type::TYPE_FLOAT), true);
	if (addr) *addr = common::PI;
}

void VariableManager::StartPreprocessing()
{
	m_Working = false;
	m_Stack.clear();
	BeginBlock();
	AddMathConstants();
}

void VariableManager::StartWorking()
{
	m_Working = true;
	m_Stack.clear();
	BeginBlock();
	AddMathConstants();
}

FunctionManager::FIND_RESULT FunctionManager::Find( const string &name, RegisteredFunction *&outRegisteredFunc, const Type *paramTypes, uint paramCount, const TypeConvRules &rules )
{
	RegisteredFunctionMap::iterator it = m_RegisteredFunctions.lower_bound(name);
	bool nameFound = false;
	uint bestParamConversionCount = MAXUINT4, currParamConversionCount;
	uint numFuncWithBestConvCount = 0;
	RegisteredFunction *bestFunc = NULL;
	while (it != m_RegisteredFunctions.end() && it->first == name)
	{
		nameFound = true;
		if (SignatureMatch(currParamConversionCount, it->second.get(), paramTypes, paramCount, rules))
		{
			if (currParamConversionCount < bestParamConversionCount)
			{
				bestParamConversionCount = currParamConversionCount;
				numFuncWithBestConvCount = 0;
				bestFunc = it->second.get();
			}
			else if (currParamConversionCount == bestParamConversionCount)
			{
				numFuncWithBestConvCount++;
				bestFunc = NULL;
			}
		}
		++it;
	}

	if (bestFunc)
	{
		outRegisteredFunc = bestFunc;
		return FIND_RESULT_OK;
	}

	outRegisteredFunc = NULL;

	if (nameFound)
	{
		if (numFuncWithBestConvCount > 1)
			return FIND_RESULT_AMBIGUOUS;
		else
			return FIND_RESULT_PARAMS_NOT_MATCH;
	}

	return FIND_RESULT_NOT_FOUND;
}

void FunctionManager::Register( const string &name, RegisteredFunctionPtr registeredFunc )
{
	RegisteredFunctionMap::iterator lowerIt = m_RegisteredFunctions.lower_bound(name);
	if (lowerIt != m_RegisteredFunctions.end() && lowerIt->first == name)
	{
		RegisteredFunctionMap::iterator upperIt = m_RegisteredFunctions.upper_bound(name);
		for (RegisteredFunctionMap::iterator it = lowerIt; it != upperIt; ++it)
			if (SignaturesConflict(it->second.get(), registeredFunc.get()))
				throw Error("Overloaded function " + name + " conflicts with existing one");
	}

	m_RegisteredFunctions.insert(RegisteredFunctionMap::value_type(name, registeredFunc));
}

void FunctionManager::RegisterIntrinsic()
{
	for (uint i = 0; i < RegisteredIntrinsicFunction::FUNC_COUNT; i++)
	{
		Register(
			RegisteredIntrinsicFunction::FUNC_NAMES[i],
			RegisteredFunctionPtr(new RegisteredIntrinsicFunction((RegisteredIntrinsicFunction::FUNC)i)) );
	}
}

bool FunctionManager::SignaturesConflict( RegisteredFunction *func1, RegisteredFunction *func2 )
{
	// TODO
	return false;
}

bool FunctionManager::SignatureMatch( uint &outParamConversionCount, RegisteredFunction *func, const Type *paramTypes, uint paramCount, const TypeConvRules &rules )
{
	outParamConversionCount = 0;

	uint formalParamCount = func->ParamTypes.size();
	assert(func->NoDefaultParamCount <= formalParamCount);
	
	if (paramCount < func->NoDefaultParamCount || paramCount > formalParamCount)
		return false;
	
	for (uint i = 0; i < paramCount; i++)
	{
		if (paramTypes[i] != func->ParamTypes[i])
		{
			if (func->ParamTypes[i].CheckConvFrom(paramTypes[i], rules) > TypeConv_Explicit)
				outParamConversionCount++;
			else
				return false;
		}
	}

	return true;
}

FunctionManager::FIND_RESULT FunctionManager::FindExactMatch( RegisteredFunction *&outRegisteredFunc, const string &name, Type resultType, const Type *paramTypes, uint paramCount )
{
	RegisteredFunctionMap::iterator it = m_RegisteredFunctions.lower_bound(name);
	bool nameFound = false;
	while (it != m_RegisteredFunctions.end() && it->first == name)
	{
		nameFound = true;
		if (SignatureExactMatch(it->second.get(), resultType, paramTypes, paramCount))
		{
			outRegisteredFunc = it->second.get();
			return FIND_RESULT_OK;
		}
		++it;
	}

	outRegisteredFunc = NULL;
	return nameFound ? FIND_RESULT_PARAMS_NOT_MATCH : FIND_RESULT_NOT_FOUND;
}

bool FunctionManager::SignatureExactMatch( RegisteredFunction *func, Type resultType, const Type *paramTypes, uint paramCount )
{
	if (func->ResultType != resultType)
		return false;

	uint formalParamCount = func->ParamTypes.size();
	assert(func->NoDefaultParamCount <= formalParamCount);
	
	if (paramCount != formalParamCount)
		return false;
	
	for (uint i = 0; i < paramCount; i++)
		if (paramTypes[i] != func->ParamTypes[i])
			return false;

	return true;
}

void FunctionManager::GetSignatures( string &out, const string &name, const string *prefix )
{
	string tmp;

	RegisteredFunctionMap::iterator it = m_RegisteredFunctions.lower_bound(name);
	while (it != m_RegisteredFunctions.end() && it->first == name)
	{
		if (prefix)
			out += *prefix;

		it->second->ResultType.GetName(tmp);
		out += tmp;
		
		out += ' ';
		out += it->first;

		out += '(';

		if (!it->second->ParamTypes.empty())
		{
			it->second->ParamTypes[0].GetName(tmp);
			out += tmp;
			for (uint i = 1; i < it->second->ParamTypes.size(); i++)
			{
				it->second->ParamTypes[i].GetName(tmp);
				out += ", ";
				out += tmp;
			}
		}

		out += ");\n";

		++it;
	}
}

bool FunctionManager::SuggestTypes( std::vector<TypeSuggestion> &outSuggestions, const string &funcName, uint actualParamCount )
{
	// Prepare clear suggestions
	outSuggestions.resize(actualParamCount);
	for (uint i = 0; i < actualParamCount; i++)
		outSuggestions[i].Clear();
	bool foundOne = false;

	uint formalParamCount;
	// For each function with given name
	for (RegisteredFunctionMap::iterator it = m_RegisteredFunctions.lower_bound(funcName)
		; it != m_RegisteredFunctions.end() && it->first == funcName
		; ++it)
	{
		formalParamCount = it->second->ParamTypes.size();
		// Parameter count fits
		if (actualParamCount >= it->second->NoDefaultParamCount && actualParamCount <= formalParamCount)
		{
			foundOne = true;
			for (uint i = 0; i < actualParamCount; i++)
			{
				// No suggestion - save type
				if (outSuggestions[i].IsEmpty())
					outSuggestions[i].T = it->second->ParamTypes[i];
				// No type, has class - AND class
				else if (outSuggestions[i].T.IsEmpty())
					outSuggestions[i].Class &= Type::TYPE_CLASSES[it->second->ParamTypes[i].GetType()];
				// Has type
				else
				{
					// Same type - nothing
					// Different type - clear type, store AND class
					if (outSuggestions[i].T != it->second->ParamTypes[i])
					{
						outSuggestions[i].Class = Type::TYPE_CLASSES[outSuggestions[i].T.GetType()]
							& Type::TYPE_CLASSES[it->second->ParamTypes[i].GetType()];
						outSuggestions[i].T.Clear();
					}
				}
			}
		}
	}

	return foundOne;
}

/*void FunctionManager::Eval(void *out, uint id, void **params)
{
	if (id = 1)
	{
		float *outVal = (float*)out;
		const float *param0 = (const float*)params[0];
		*outVal = sinf(*param0);
	}
	else
		assert(0);
}

void FunctionManager::EvalVariadic(void *out, uint id, void **params, const Type *paramTypes, uint paramCount)
{
	if (id == 2)
	{
		for (uint i = 0; i < paramCount; i++)
			std::cout << *(string*)params[i];
	}
	else
		assert(0);
}*/

PreprocessContext::PreprocessContext()
: m_FlagStack(1)
{
	m_FlagStack[0] = 0;
}

void RegisteredRegScriptFunction::Exec( void *out, void **params, uint paramCount )
{
	assert(Def);
	Def->Call(out, params, paramCount);
}

RegisteredRegScriptFunction::RegisteredRegScriptFunction( FunctionDef *def )
: Def(def)
{
	assert(Def);

	ResultType = Def->ResultType;

	uint paramCount = Def->ParamDefs.size();
	ParamTypes.resize(paramCount);
	NoDefaultParamCount = 0;
	for (uint i = 0; i < paramCount; i++)
	{
		ParamTypes[i] = Def->ParamDefs[i]->ExprType;
		if (Def->ParamDefs[i]->InitExpr.is_null())
			NoDefaultParamCount++;
	}
}

const char * RegisteredIntrinsicFunction::FUNC_NAMES[] = {
	"sin",
	"print",
};

void RegisteredIntrinsicFunction::Exec( void *out, void **params, uint paramCount )
{
	switch (Func)
	{
	case FUNC_SIN:
		*(float*)out = sinf( *(float*)params[0] );
		break;
	case FUNC_PRINT:
		assert(out == NULL);
		std::cout << *(string*)params[0];
		break;
	default:
		assert(0);
	}
}

RegisteredIntrinsicFunction::RegisteredIntrinsicFunction( FUNC func )
: Func(func)
{
	switch (Func)
	{
	case FUNC_SIN:
		ResultType = Type(Type::TYPE_FLOAT);
		ParamTypes.resize(1);
		ParamTypes[0] = Type(Type::TYPE_FLOAT);
		NoDefaultParamCount = 1;
		break;
	case FUNC_PRINT:
		ResultType = Type(Type::TYPE_VOID);
		NoDefaultParamCount = 1;
		ParamTypes.resize(1);
		ParamTypes[0] = Type(Type::TYPE_STRING);
		break;
	default:
		assert(0);
	}
}

void Context::StartPreprocessing()
{
	FuncMan.RegisterIntrinsic();
	VarMan.StartPreprocessing();
}
